// Octopod (Controls Transfer of Pieces of Data)
// Copyright (c) 2005-2010 Barath Raghavan and James Wilcox.  All rights reserved.
//
// GPL goes here
//
// This file contains network transport base classes and I/O routines.

#ifndef OCTOPOD_SOCKET_H__
#define OCTOPOD_SOCKET_H__

#include <iostream>

extern "C" {
#include <sys/socket.h>
}

namespace octopod {

// Represents a host's network address and port number.  Wraps QHostAddress
// but can also store information about an intermediate Network Address Translator
// that is in front of that host address.
class NetworkAddress {
  public:
    // Differentiate between IPv4 and IPv6
    enum IPVersion { kIPv4 = AF_INET, kIPv6 = AF_INET6 };
    static const char *validchars;

    // Creates an empty network address
    NetworkAddress() : hostaddr_(""), nataddr_(""), port_(0) { };
    ~NetworkAddress();

    std::string hostaddr() const { return hostaddr_; }
    std::string nataddr() const { return nataddr_; }
    uint16_t port() const { return port_; }

    // Sets the host address represented. The given string hostaddr should be an
    // IP address.  If the address is IPv4, it should be in the N.N.N.N format.
    // If the address is IPv6, it should be in the N:N:...:N format. 
    // Returns true upon success.
    bool set_hostaddr(const std::string &hostaddr); 

    // Sets the NAT address represented. The format of the given string nataddr
    // is the same as for set_hostaddr().
    // Returns true upon success.
    bool set_nataddr(const std::string &nataddr);

    // Sets the port represented.
    void set_port(uint16_t port) { port_ = port; }

    // Attempts to determine the IP version of this address.
    // Assumes IPv6 if either the hostaddr or nataddr are IPv6.
    IPVersion version() const { 
      return (hostaddr_.find(':') != std::string::npos ||
              nataddr_.find(':') != std::string::npos) ? kIPv6 : kIPv4;
    }

    // Returns true if the given string is likely a parseable address.
    // Only checks whether the chars in the string are valid.
    bool validAddress(const std::string &addr);

  private:
    std::string hostaddr_;
    std::string nataddr_;

    uint16_t port_;

    DISALLOW_COPY_AND_ASSIGN(NetworkAddress);
};

// A non-instantiable base class for any network transport socket type used in
// Octopod.  Any code that needs to send or receive data over the network should
// keep a pointer to this class type rather than any subclass.
class Socket {
  public:
    // The possible current states of the socket.  kUnknown indicates that
    // the socket has failed in some unrecoverable way.
    enum SocketState { kDisconnected, kConnected, kUnknown };

    // The possible return values from transport socket calls.
    enum SocketError { kOk = 0, kFail };

    // Types of sockets we know about.
    enum SocketTypes { kTCPSocket };
    
    Socket() { }
    virtual ~Socket() { }

    // Returns the current state of this socket.
    virtual SocketState state() const = 0;

    // Attempts to connect this socket to the given address.
    // Returns kOk if the socket was disconnected and the connection succeeds.
    // Returns kFail otherwise.
    // Other error conditions?
    virtual SocketError connect(const NetworkAddress &addr) = 0;

    // Attempts to disconnect this socket; first flushes any outgoing data.
    // Returns kOk if the socket was connected and the outgoing data flush and
    // close succeeds.  Returns kFail otherwise.
    // Other error conditions?
    virtual SocketError disconnect() = 0;

    // Performs a blocking send of size bytes from data.
    // Returns the number of bytes written to the underlying send buffer
    // upon success and -1 otherwise.
    virtual int send(const char *data, int size) = 0;

    // Performs a blocking receive of at most size bytes into data.
    // Returns the number of bytes read into data upon success and
    // -1 otherwise.
    virtual int recv(int size, char *data) = 0;
    
    // Performs a complete blocking send of all the bytes stored
    // in string data.  data.size() must be at most INT_MAX.
    virtual int send(const std::string &data);

    // Performs a complete blocking read.
    virtual int recv(int size, std::string *data);

  private:
    DISALLOW_COPY_AND_ASSIGN(Socket);
};

// A non-instantiable base class for any network transport server socket.
// Any code that needs to listen for incoming connections should keep a pointer
// to this class type rather than any subclass.
class ListenSocket {
  public:
    // The possible current states of the socket.
    enum SocketState { kDisconnected, kListening, kUnknown };

    // Creates a new listening socket.
    ListenSocket() { }

    // Closes the listening socket and cleans up any state.
    virtual ~ListenSocket() { }

    // Attempts to initiate listening on this listen socket at the given address
    // and port.  If the port given is 0, attempts to listen on a
    // system-assigned port.
    // Returns kOk if the socket is successfully set to listening.
    virtual Socket::SocketError listen(uint16_t port) = 0;

    // Blocks to accept a new inbound connection and returns its newly-assigned
    // socket.  Caller is responsible for cleaning up the returned Socket.
    // Returns NULL upon error.
    virtual Socket *accept() = 0;

  private:
    DISALLOW_COPY_AND_ASSIGN(ListenSocket);
};

}  // namespace octopod

#endif  // OCTOPOD_SOCKET_H__
